package binding

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"
)

const (
	TimeFormat = "time_format"
	Unix       = "unix"
	UnixNano   = "unixnano"
	// 如果还有其他重复出现的字符串也可以在此处定义
)

var (
	errUnknownType = errors.New("unknown type")

	// ErrConvertMapStringSlice can not convert to map[string][]string
	ErrConvertMapStringSlice = errors.New("can not convert to map slices of strings")

	// ErrConvertToMapString can not convert to map[string]string
	ErrConvertToMapString = errors.New("can not convert to map of strings")
)

type QueryBind struct{}

func (j QueryBind) Name() string {
	return "Query"
}

func (j QueryBind) Bind(req *http.Request, any interface{}) error {
	v := req.URL.Query()
	return j.mapping(any, v)
}

/*
	如果 PkgPath 是一个非空字符串，那么对应的字段就是未导出的
	sf.Anonymous 是一个布尔值，表示对应的字段是否为匿名字段
	type Person struct {
	    name string
	}

	type Employee struct {
	    Person
	    company string
	}
	在这段代码中，Person 就是 Employee 的一个匿名字段。这意味着 Employee 结构体会直接包含 Person 的所有字段，并且可以直接访问 Person 的字段和方法。

	如果你获取 Employee 类型的反射对象，并迭代其每个字段，那么 Person 字段的 StructField 对象的 Anonymous 字段将会返回 true，表示它是一个匿名字段。
	在这个例子中，Employee 结构体有两个字段：一个是 person，另一个是 company。

	当你获取 Employee 的反射对象并迭代其每个字段时：

	对于 person 字段：

	sf.Anonymous 将返回 true，因为 person 是一个匿名字段。
	sf.PkgPath 将返回非空字符串，因为 person 是未导出的（小写字母开头）。即使它是 Employee 的匿名字段，我们仍然不能通过反射修改它的值或访问其方法（除非在同一包内）。
	对于 company 字段：

	sf.Anonymous 将返回 false，因为 company 不是匿名字段。
	sf.PkgPath 将返回空字符串，因为 company 是已导出的字段（假设 Employee 结构体定义在当前包内，且 company 字段首字母大写）。
	需要注意的是，虽然我们不能直接访问或修改 person 字段的值，但是如果该字段有已导出的字段或方法，我们可以通过 Employee 来访问或修改它们。这是由于 Go 语言的特性，匿名字段的已导出字段和方法会自动提升到包含该匿名字段的结构体
*/

/*
vPtr.Elem()是Go语言反射包中的一个方法，用于返回指针所指向的值的Value表示。它通常用于获取指针指向的实际元素，并对该元素进行操作。

在你提供的代码中，vPtr := value将value赋值给了vPtr。如果value在这里是一个指针类型，则vPtr.Elem()会获取到该指针所指向的元素的 Value 表示，然后可以通过这个 Value 对象来读取或者修改原始对象的值。

例如：



x := 2               // 定义一个整数 x
v := reflect.ValueOf(&x) // 获取x的指针的反射Value对象
fmt.Println(v.Elem().Interface())  // 输出2，通过v.Elem()获取到原始的x值
注意：Elem()只能用于指针、数组、切片、Map和Chan类型的reflect.Value，如果使用非法类型调用，程序会panic。因此，在使用前，你需要确认变量的Kind是否为上述类型之一。
*/

/*
reflect.StructField 结构体表示了结构体的一个字段的信息。在这份代码中，它通过 tValue.Field(i) 得到。

sf.PkgPath 是字段所在包的路径。如果这个字段是导出的（即可以在包外部访问），PkgPath 就是空字符串。否则，PkgPath 就是非空字符串。所以 sf.PkgPath != "" 用于检查这个字段是否是未导出的（即私有的）。

sf.Anonymous 表示该字段是否为匿名字段。匿名字段，也称之为内嵌字段或嵌入字段，是指一个结构体字段没有显式的字段名，只有字段类型。!sf.Anonymous 检查字段是否不是匿名字段。

所以 if sf.PkgPath != "" && !sf.Anonymous 的意思是：“如果当前字段是未导出的，并且它不是匿名字段，就跳过当前字段的处理。”

例如：



type Person struct {
    Name string "tag:name"
    Age  int    "tag:age"
    privateField string // 这是一个未导出的字段
}

type AnonymousPerson struct {
    Person        // 这是一个匿名字段
    FavoriteColor string "tag:color"
}
对于 Person 结构体，privateField 会被跳过；对于 AnonymousPerson 结构体，Person 字段会被跳过。
*/

func mapFormToStruct(value reflect.Value, form map[string][]string, field reflect.StructField, tag string) (bool, error) {
	if field.Tag.Get(tag) == "-" {
		return false, nil
	}

	vKind := value.Kind()
	if vKind == reflect.Ptr {
		var isNew bool
		vPtr := value
		if value.IsNil() {
			isNew = true
			vPtr = reflect.New(value.Type().Elem())
		}

		isSet, err := mapFormToStruct(vPtr.Elem(), form, field, "form")
		if err != nil {
			return false, err
		}
		if isNew && isSet {
			value.Set(vPtr)
		}
		return true, err
	}

	// 字段是结构体但不是匿名字段
	//if vKind != reflect.Struct || !field.Anonymous {
	if vKind != reflect.Struct {
		setter := NewFieldSetter(value, field, form, tag)
		return setter.TrySetValue()
		//return trySetValue(value, field, form, tag)
	}

	if vKind == reflect.Struct {
		tValue := value.Type()
		var isSet bool
		for i := 0; i < value.NumField(); i++ {
			tf := tValue.Field(i)
			if tf.PkgPath != "" && !tf.Anonymous {
				continue
			}
			ok, err := mapFormToStruct(value.Field(i), form, tf, tag)
			if err != nil {
				return false, err
			}
			isSet = isSet || ok
		}
		return isSet, nil
	}

	return false, nil
}

/*
在 Go 的 reflect 包中，Elem() 方法返回一个类型的元素类型。这是什么意思呢？

首先，你要知道并非所有类型都有元素类型，只有以下几种类型才有元素类型：

Array: 数组的元素类型就是数组中每个元素的类型。
Chan: 通道的元素类型就是通道中传输的值的类型。
Map: 对于 map 类型，元素类型就是它的值的类型（注意不是键的类型）。
Ptr: 指针的元素类型就是指针指向的值的类型。
Slice: 切片的元素类型就是切片内部元素的类型
*/
func mapFormToMap(any interface{}, form map[string][]string) error {
	el := reflect.TypeOf(any).Elem()
	if el.Kind() == reflect.Slice {
		ptrMap, ok := any.(map[string][]string)
		if !ok {
			return ErrConvertMapStringSlice
		}
		for k, v := range form {
			ptrMap[k] = v
		}
		return nil
	}

	ptrMap, ok := any.(map[string]string)
	if !ok {
		return ErrConvertToMapString
	}

	for k, v := range form {
		ptrMap[k] = v[0] // pick first
	}

	return nil

}

func (j QueryBind) mapping(any interface{}, form map[string][]string) error {
	value := reflect.ValueOf(any)
	if value.Kind() != reflect.Ptr {
		return fmt.Errorf("input must be a pointer")
	}
	ptrVal := value
	if value.Kind() == reflect.Ptr {
		ptrVal = value.Elem()
	}

	switch ptrVal.Kind() {
	case reflect.Map:
		if ptrVal.Type().Key().Kind() == reflect.String {
			return mapFormToMap(ptrVal.Interface(), form)
		} else {
			return fmt.Errorf("map keys must be of type string")
		}
	default:
		_, err := mapFormToStruct(reflect.ValueOf(any), form, reflect.StructField{}, "form")
		return err
	}
}

func head(str, sep string) (head string, tail string) {
	idx := strings.Index(str, sep)
	if idx < 0 {
		return str, ""
	}
	return str[:idx], str[idx+len(sep):]
}

type FieldSetter struct {
	value        reflect.Value
	field        reflect.StructField
	form         map[string][]string
	tag          string
	defaultValue string
}

func NewFieldSetter(value reflect.Value, field reflect.StructField, form map[string][]string, tag string) *FieldSetter {
	fs := &FieldSetter{
		value: value,
		field: field,
		form:  form,
		tag:   tag,
	}

	tagValue := fs.field.Tag.Get(fs.tag)
	tagValue, opts := head(tagValue, ",")
	if tagValue == "" {
		tagValue = field.Name
	}
	fs.tag = tagValue

	var defaultValue string
	if len(opts) > 0 {
		opt, _ := head(opts, ",")
		if k, v := head(opt, "="); k == "default" {
			defaultValue = v
		}
	}

	fs.defaultValue = defaultValue

	return fs
}

func (fs *FieldSetter) TrySetValue() (bool, error) {
	vs, ok := fs.form[fs.tag]
	if !ok && fs.defaultValue == "" {
		return false, nil
	}
	switch fs.value.Kind() {
	case reflect.Slice:
		if !ok {
			vs = []string{fs.defaultValue}
		}
		return true, setSlice(fs.value, vs, fs.field)
	case reflect.Array:
		if !ok {
			vs = []string{fs.defaultValue}
		}
		if len(vs) != fs.value.Len() {
			return false, fmt.Errorf("%q is not valid value for %s", vs, fs.value.Type().String())
		}
		return true, setArray(fs.value, vs, fs.field)
	default:
		var val string
		if len(vs) > 0 {
			val = vs[0]
		} else if !ok {
			val = fs.defaultValue
		}
		return true, setWithProperType(fs.value, val, fs.field)
	}
}

func trySetValue(value reflect.Value, field reflect.StructField, form map[string][]string, tag string) (bool, error) {
	tagValue := field.Tag.Get(tag)
	tagValue, opts := head(tagValue, ",")
	if tagValue == "" {
		tagValue = field.Name
	}
	var defaultValue string
	if len(opts) > 0 {
		opt, _ := head(opts, ",")
		if k, v := head(opt, "="); k == "default" {
			defaultValue = v
		}
	}
	vs, ok := form[tagValue]
	if !ok && defaultValue == "" {
		return false, nil
	}
	switch value.Kind() {
	case reflect.Slice:
		if !ok {
			vs = []string{defaultValue}
		}
		return true, setSlice(value, vs, field)
	case reflect.Array:
		if !ok {
			vs = []string{defaultValue}
		}
		if len(vs) != value.Len() {
			return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
		}
		return true, setArray(value, vs, field)
	default:
		var val string
		if len(vs) > 0 {
			val = vs[0]
		} else if !ok {
			val = defaultValue
		}
		return true, setWithProperType(value, val, field)
	}
}

func setArray(value reflect.Value, vals []string, field reflect.StructField) error {
	for i, val := range vals {
		err := setWithProperType(value.Index(i), val, field)
		if err != nil {
			return err
		}
	}
	return nil
}

func setSlice(value reflect.Value, vals []string, field reflect.StructField) error {
	slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
	if err := setArray(value, vals, field); err != nil {
		return err
	}
	value.Set(slice)
	return nil
}

func setWithProperType(value reflect.Value, val string, field reflect.StructField) error {
	switch value.Kind() {
	case reflect.Int:
		return setIntField(val, 0, value)
	case reflect.Int8:
		return setIntField(val, 8, value)
	case reflect.Int16:
		return setIntField(val, 16, value)
	case reflect.Int32:
		return setIntField(val, 32, value)
	case reflect.Int64:
		switch value.Interface().(type) {
		case time.Duration:
			return setTimeDuration(val, value)
		}
		return setIntField(val, 64, value)
	case reflect.Uint:
		return setUintField(val, 0, value)
	case reflect.Uint8:
		return setUintField(val, 8, value)
	case reflect.Uint16:
		return setUintField(val, 16, value)
	case reflect.Uint32:
		return setUintField(val, 32, value)
	case reflect.Uint64:
		return setUintField(val, 64, value)
	case reflect.Bool:
		return setBoolField(val, value)
	case reflect.Float32:
		return setFloatField(val, 32, value)
	case reflect.Float64:
		return setFloatField(val, 64, value)
	case reflect.String:
		value.SetString(val)
	case reflect.Struct:
		switch value.Interface().(type) {
		case time.Time:
			return setTimeField(val, field, value)
		}
		return json.Unmarshal([]byte(val), value.Interface())
	case reflect.Map:
		return json.Unmarshal([]byte(val), value.Addr().Interface())
	default:
		return errUnknownType
	}

	return nil
}

func parseTimeFormat(val string, timeFormat string, loc *time.Location) (time.Time, error) {
	switch tf := strings.ToLower(timeFormat); tf {
	case "unix", "unixnano":
		tv, err := strconv.ParseInt(val, 10, 64)
		if err != nil {
			return time.Time{}, err
		}

		d := time.Duration(1)
		if tf == "unixnano" {
			d = time.Second
		}

		return time.Unix(tv/int64(d), tv%int64(d)), nil
	default:
		if val == "" {
			return time.Time{}, nil
		}

		return time.ParseInLocation(timeFormat, val, loc)
	}
}

func setTimeField(val string, structField reflect.StructField, value reflect.Value) error {
	timeFormat := structField.Tag.Get("time_format")
	if timeFormat == "" {
		timeFormat = time.RFC3339
	}

	l := time.Local
	if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
		l = time.UTC
	}

	if locTag := structField.Tag.Get("time_location"); locTag != "" {
		loc, err := time.LoadLocation(locTag)
		if err != nil {
			return err
		}
		l = loc
	}

	t, err := parseTimeFormat(val, timeFormat, l)
	if err != nil {
		return err
	}

	value.Set(reflect.ValueOf(t))
	return nil
}

func setIntField(val string, bitSize int, field reflect.Value) error {
	if val == "" {
		val = "0"
	}
	intVal, err := strconv.ParseInt(val, 10, bitSize)
	if err == nil {
		field.SetInt(intVal)
	}
	return err
}

func setUintField(val string, bitSize int, field reflect.Value) error {
	if val == "" {
		val = "0"
	}
	uintVal, err := strconv.ParseUint(val, 10, bitSize)
	if err == nil {
		field.SetUint(uintVal)
	}
	return err
}

func setBoolField(val string, field reflect.Value) error {
	if val == "" {
		val = "false"
	}
	boolVal, err := strconv.ParseBool(val)
	if err == nil {
		field.SetBool(boolVal)
	}
	return err
}

func setTimeDuration(val string, value reflect.Value) error {
	d, err := time.ParseDuration(val)
	if err != nil {
		return err
	}
	value.Set(reflect.ValueOf(d))
	return nil
}

func setFloatField(val string, bitSize int, field reflect.Value) error {
	if val == "" {
		val = "0.0"
	}
	floatVal, err := strconv.ParseFloat(val, bitSize)
	if err == nil {
		field.SetFloat(floatVal)
	}
	return err
}
